#pragma once
#ifndef _KH_ENGINE_ACTOR_H
#define _KH_ENGINE_ACTOR_H
#include <Kharlia/Engine/Common.h>
#include <Kharlia/Engine/Units.h>
#include <Kharlia/Engine/Drawable.h>
#include <Kharlia/Engine/Net.h>
#include <Kharlia/Core/Tasker.h>

#define KH_DEF_SET_NV(TYPE, NAME) inline void Set##NAME(TYPE InValue) { m##NAME = InValue; mNetDirty = true; }
#define KH_DEF_GETSET_NV(TYPE, NAME) KH_DEF_GET(TYPE, NAME) KH_DEF_SET_NV(TYPE, NAME)

#undef Yield // From WinBase.h

#define DECL_REP() \
    virtual sint16* GetNativeRepVars(sint16* indices, byte const* recent, \
                                     FFieldInfo const** fields, ANetwork const* net, \
                                     CActor const* act)

namespace kharlia { namespace engine {

void _InitActorClasses();
void _ExitActorClasses();
class ATransform;
void _MemberTest(TPtr<ATransform> t);

class CLevel;
class CWorld;
class CEngine;
class CClient;
class CActorReplicator;
class CPlayer;
class APawn;
class FTexture;
class CSpriteAniDef;

namespace EDirection { enum Type {
    NORTH,
    WEST,
    SOUTH,
    EAST,
    NORTHWEST,
    SOUTHWEST,
    SOUTHEAST,
    NORTHEAST
};}

union FTileGroupNumber {
    uint16 Number;
    struct {
        uint8 X;
        uint8 Y;
    };

    FTileGroupNumber(): Number(0) {}
    FTileGroupNumber(uint16 n): Number(n) {}

    inline bool operator==(FTileGroupNumber const& other) const { return Number == other.Number; }
    inline bool operator!=(FTileGroupNumber const& other) const { return Number != other.Number; }
    inline operator uint16() const { return Number; }
};

class CActor;
class CGame;
class ANetwork;
class APlayerController;

/// Base class for all actor components.
/// Note that all component classes are prefixed with "A" rather than "C".
class KH_ENGINE_API AComponent: public core::CObject {
    /// The actor this component is bound to.
    /// The component is considered uninitialized if this is None.
    TWeakPtr<CActor> mActor;
    /// Convenient access to the owner's level.
    TWeakPtr<CLevel> mLevel;
    /// True when this component, and therefore the actor, shouldn't be
    /// deleted while the game is started.
    bool mNoDelete;
    /// Tick priority of this component. Lower priority components are
    /// ticked first. A component that depends on another component should
    /// higher priority number.
    sint16 mPriority;

    // Setters for Python
    void SetActor(TPtr<CActor> owner) { mActor = owner; }
    void SetLevel(TPtr<CLevel> level) { mLevel = level; }

    KH_DECL_GC()

protected:
    /// True when a network replicated variable has been changed by
    /// Python or C++.
    bool mNetDirty;
    /// True when it is the component's first time to be replicated.
    bool mNetInitial;
    /// True when the machine we're replicating to owns this component
    bool mNetOwner;
    /// True if this component should be tested individually for relevancy
    bool mNetTestRelevancy;

    uint8 mNetID;

    void InitImpl(TPtr<CActor> owner);
    void ExitImpl();

    /// A check to ensure a required component is defined.
    /// Raises an exception with type information if ref is empty.
    template <class T>
    void EnsureComponent(TPtr<T>& ref, bool create=false);
    void EnsureComponent(TPtr<AComponent>& ref, std::type_info const& reftype,
                         bool create=false);

    KH_DEF_SET(bool, NoDelete)
    KH_DEF_SET(sint16, Priority)
    DECL_CLASSINFO()

public:
    AComponent(PyObject* self);
    virtual ~AComponent();

    /// Initialize the component with its owner
    void Init(TPtr<CActor> owner);
    /// Finalize the component, clears the owner
    void Exit();
    /// Standard update
    virtual void Tick(float deltaTime);

    DECL_REP();
    virtual void OnReplicated(FFieldInfo const* field, FFieldInfo const** fields) {}

    /// Notify the component the game has started
    void OnGameStarted();
    /// Notify the component that the game has stopped
    void OnGameStopped();

    /// Returns a handle to the instance's Python type.
    handle<PyTypeObject> GetTypeHandle() const;

    /// True if the component has been initialized
    bool GetInited() const;

    void ResetLevel();

    TPtr<CActor> GetActor() const {
        return mActor.Lock();
    }
    TPtr<CLevel> GetLevel() const {
        return mLevel.Lock();
    }
    bool GetNoDelete() const { return mNoDelete; }
    sint16 GetPriority() const { return mPriority; }
    bool GetNetDirty() const { return mNetDirty; }
    bool GetNetInitial() const { return mNetInitial; }
    bool GetNetOwner() const { return mNetOwner; }
    bool GetNetTestRelevancy() const { return mNetTestRelevancy; }

    virtual bool GetNetIsRelevantFor(
        TPtr<CActor> const& realViewer,
        TPtr<CActor> const& viewer,
        FVector3F const& position
        )
    {
        return true;
    }

    /// Comparison to allow sorting by priority
    bool operator<(AComponent const& other) const { return mPriority < other.mPriority; }

    friend void _InitActorClasses();
    friend void _InitActorBindings();
    friend class ANetwork;
};

inline void AComponent::Init(TPtr<CActor> owner) {
    call_method<void>(mSelf, "Init", owner);
}

inline void AComponent::Exit() {
    call_method<void>(mSelf, "Exit");
}

inline bool AComponent::GetInited() const {
    return mActor;
}

inline handle<PyTypeObject> AComponent::GetTypeHandle() const {
    return handle<PyTypeObject>(borrowed(Py_TYPE(mSelf)));
}

template <class T>
inline void AComponent::EnsureComponent(TPtr<T>& ref, bool create) {
    EnsureComponent(ref.CastPtrStatic<AComponent>(), typeid(T), create);
}

inline void AComponent::OnGameStarted() {
    call_method<void>(mSelf, "OnGameStarted");
}

inline void AComponent::OnGameStopped() {
    call_method<void>(mSelf, "OnGameStopped");
}

/// A transform component representing an actor's position in the world.
/// Will support scale and rotation in the future if necessary.
class KH_ENGINE_API ATransform: public AComponent {
    /// The actor's position.
    FVector3F mPosition;
    /// A tile group number created from the position within the level.
    /// This is used to speed up searches for actors in specific locations.
    FTileGroupNumber mTileGroupNumber;

    /// Update the tile group number based on Position.
    void ResetTileGroupNumber();

protected:
    DECL_CLASSINFO()

public:
    ATransform(PyObject* self): AComponent(self), mTileGroupNumber(0) {}
    virtual ~ATransform() {}

    //virtual void InitImpl(TPtr<CActor> owner);
    //virtual void ExitImpl();
    virtual void Tick(float deltaTime) {}
    DECL_REP();
    
    /// Move the actor by delta.
    /// Despite this being defined in AMovement, the actual
    /// movement function requires transform and physics components.
    bool Move(FVector3F const& delta, bool smooth=true);

    KH_DEF_GETSET_NV(FVector3F const&, Position)
    void AddPosition(FVector3F const& delta);
    KH_DEF_GET(FTileGroupNumber, TileGroupNumber)

    /// Returns the tile group number for pos.
    static FTileGroupNumber CalculateTileGroupNumber(FVector3F const& pos);

    friend void _InitActorClasses();
    friend void _InitActorBindings();
    friend void _MemberTest(TPtr<ATransform> t);
};

inline void ATransform::ResetTileGroupNumber() {
    mTileGroupNumber = CalculateTileGroupNumber(mPosition);
}

inline void ATransform::AddPosition(FVector3F const& delta) {
    mPosition += delta;
    mNetDirty = true;
}

inline FTileGroupNumber ATransform::CalculateTileGroupNumber(FVector3F const& pos) {
    FTileGroupNumber n;
    n.X = int(pos.X) / 64;
    n.Y = int(pos.Y) / 64;
    return n;
}

/// A movement component. Stores information about the actor's velocity and
/// acceleration.
class KH_ENGINE_API AMovement: public AComponent {
    /// Velocity in tiles per second.
    FVector3F mVelocity;
    /// Acceleration applied to velocity during Tick().
    FVector3F mAcceleration;
    /// Maximum allowed velocity in absolute values.
    FVector3F mMaxVelocity;
    /// Maximum allowed acceleration in absolute values.
    FVector3F mMaxAcceleration;

protected:
    void InitImpl(TPtr<CActor> owner);
    void ExitImpl();

    DECL_CLASSINFO()

public:
    AMovement(PyObject* self): AComponent(self) {}
    virtual ~AMovement() {}

    virtual void Tick(float deltaTime);
    DECL_REP();

    KH_DEF_GETSET_NV(FVector3F const&, Velocity)
    KH_DEF_GETSET_NV(FVector3F const&, Acceleration)
    KH_DEF_GETSET_NV(FVector3F const&, MaxVelocity)
    KH_DEF_GETSET_NV(FVector3F const&, MaxAcceleration)

    friend void _InitActorClasses();
    friend void _InitActorBindings();
};

/// Rendering component base class. Subclasses provide information that the engine needs
/// to render the actor.
class KH_ENGINE_API ARender: public AComponent, public FDrawable {
    /// Whether or not to render the component
    bool mHidden;

protected:
    void InitImpl(TPtr<CActor> owner);
    void ExitImpl();

    DECL_CLASSINFO()

public:
    ARender(PyObject* self);
    virtual ~ARender();

    virtual void Tick(float deltaTime);
    /// Draw this component's actor using the engine's render interface
    virtual void Draw(CClient* client) {}

    bool GetHidden() const { return mHidden; }
    void SetHidden(bool v) { mHidden = v; mNetDirty = true; }
    
    friend void _InitActorClasses();
    friend void _InitActorBindings();
};

class KH_ENGINE_API ASprite: public ARender {
    shared_ptr<FTexture> mImage;
    EDirection::Type mDirection;

protected:
    void InitImpl(TPtr<CActor> owner);
    void ExitImpl();

    DECL_CLASSINFO()

public:
    ASprite(PyObject* self);
    virtual ~ASprite();

    virtual void Tick(float deltaTime);
    virtual void Draw(CClient* client);

    shared_ptr<FTexture> const& GetImage() const { return mImage; }
    void SetImage(shared_ptr<FTexture> const& v) { mImage = v; mNetDirty = true; }
    EDirection::Type GetDirection() const { return mDirection; }
    void SetDirection(EDirection::Type v) { mDirection = v; mNetDirty = true; }

    friend void _InitActorClasses();
    friend void _InitActorBindings();
};

struct FSpriteFrame {
    FRectI Rect;
    float Time;
};

class KH_ENGINE_API ASpriteAnimation: public ASprite {
public:
    //typedef std::vector<FSpriteFrame> FrameList;

    ASpriteAnimation(PyObject* self);
    virtual ~ASpriteAnimation();

    virtual void Tick(float deltaTime);
    virtual void Draw(CClient* client);
    virtual uint16 GetDirectionFrameList(EDirection::Type dir) const;

    uint16 GetCurrentFrameList() const { return mCurrentFrameList; }
    void SetCurrentFrameList(uint16 index);
    uint16 GetCurrentFrame() const { return mCurrentFrame; }
    void SetCurrentFrame(uint16 index);

    TPtr<CSpriteAniDef> const& GetDefinition() const { return mDefinition; }
    void SetDefinition(TPtr<CSpriteAniDef> const& def);
    bool IsDefinitionLoaded() const;
    bool GetShouldMapToDirections() const { return mShouldMapToDirections; }
    void SetShouldMapToDirections(bool value) { mShouldMapToDirections = value; mNetDirty = true; }

protected:
    void InitImpl(TPtr<CActor> owner);
    void ExitImpl();

private:
    uint16 mCurrentFrameList;
    uint16 mCurrentFrame;
    float mCurrentTime;
    bool mShouldMapToDirections;
    TPtr<CSpriteAniDef> mDefinition;

    friend void _InitActorClasses();
    friend void _InitActorBindings();
};

/// A physics component. Provides the actor's bounding box along with
/// information about what it can collide with.
class KH_ENGINE_API ACollider: public AComponent {
    /// True when the actor should collide with tiles
    bool mCollideTiles;
    /// True when the actor should collide with actors
    bool mCollideActors;
    /// True when the actor should block other actors
    bool mBlockActors;
    /// The layer of the level to be used for collision checks
    int mCollisionLayer;
    /// The actor's bounding box, can have negative offset.
    FRectF mBounds;
    /// The time when this actor last collided, used to prevent too
    /// many calls to collision events.
    float mLastActorHitTime;
    /// The actor we hit last. This pointer is only used for equality checking
    /// and should never be used
    // TODO: Make this store the actor's name hash instead.
    CActor* mLastActorHit; // used only for checking in MoveActor()

    std::vector<TWeakPtr<ACollider>> mTouchers;

protected:
    void InitImpl(TPtr<CActor> owner);
    void ExitImpl();
    void BeginTouchImpl(TPtr<ACollider> other);
    void EndTouchImpl(TPtr<ACollider> other);
    void BumpImpl(TPtr<ACollider> other);
    int GetToucherIndex(TPtr<ACollider> other) const;
    void AddToucher(TPtr<ACollider> other);

    DECL_CLASSINFO()

public:
    ACollider(PyObject* self);
    virtual ~ACollider() {}

    virtual void Tick(float deltaTime) {}

    /// Called when this collides with another or is collided into by
    /// another.
    void BeginTouch(TPtr<ACollider> other) {
        call_method<void>(mSelf, "BeginTouch", other);
    }
    void EndTouch(TPtr<ACollider> other) {
        call_method<void>(mSelf, "EndTouch", other);
    }
    void Bump(TPtr<ACollider> other) {
        call_method<void>(mSelf, "Bump", other);
    }

    void EndTouches();

    bool IsTouching(TPtr<ACollider> other) const;

    int GetToucherCount() const {
        return mTouchers.size();
    }

    TPtr<ACollider> GetToucher(int index) const {
        return mTouchers.at(index).Lock();
    }

    std::vector<TWeakPtr<ACollider>> const& GetTouchers() const {
        return mTouchers;
    }

    KH_DEF_GETSET_NV(bool, CollideTiles)
    KH_DEF_GETSET_NV(bool, CollideActors)
    bool GetBlockActors() const { return mBlockActors; }
    void SetBlockActors(bool value) { mBlockActors = value; mNetDirty = true; }
    KH_DEF_GETSET_NV(int, CollisionLayer)
    KH_DEF_GETSET_NV(FRectF const&, Bounds)
    
    friend void _InitActorClasses();
    friend void _InitActorBindings();
    friend class CLevel;
};

class KH_ENGINE_API ACamera: public AComponent {
    TWeakPtr<ATransform> mTarget;
    FVector3F mTargetOffset;

    KH_DECL_GC()

protected:
    void InitImpl(TPtr<CActor> owner);
    void ExitImpl();

    DECL_CLASSINFO()

public:
    ACamera(PyObject* self): AComponent(self) {}
    virtual ~ACamera() {}

    virtual void Tick(float deltaTime) {}
    DECL_REP();

    TPtr<ATransform> GetTarget() const { return mTarget.Lock(); }
    void SetTarget(TPtr<ATransform> const& value) { mTarget = value; mNetDirty = true; }
    KH_DEF_GETSET_NV(FVector3F const&, TargetOffset)

    friend void _InitActorClasses();
    friend void _InitActorBindings();
};

class APawn;

class KH_ENGINE_API AController: public AComponent {
    TWeakPtr<APawn> mPawn;

    KH_DECL_GC()

protected:
    void InitImpl(TPtr<CActor> owner);
    void ExitImpl();

    void SetPawn(TPtr<APawn> const& value) { mPawn = value; mNetDirty = true; }

    DECL_CLASSINFO()

public:
    AController(PyObject* self);
    virtual ~AController() {}
    
    virtual void Tick(float deltaTime);
    DECL_REP();
    virtual void OnReplicated(FFieldInfo const* field, FFieldInfo const** fields);

    void PossessImpl(TPtr<APawn> pawn) {}
    void UnpossessImpl() {}
    void Possess(TPtr<APawn> pawn) {
        call_method<void>(mSelf, "Possess", pawn);
    }
    void Unpossess() {
        call_method<void>(mSelf, "Unpossess");
    }

    TPtr<APawn> GetPawn() const { return mPawn.Lock(); }

    friend void _InitActorClasses();
    friend void _InitActorBindings();
};

class KH_ENGINE_API APlayerController: public AController {
    TWeakPtr<CPlayer> mPlayer;
    TWeakPtr<ACamera> mCamera;

    KH_DECL_GC()

protected:
    void InitImpl(TPtr<CActor> owner);
    void ExitImpl();
    void TickImpl(float deltaTime);

    DECL_CLASSINFO()

public:
    APlayerController(PyObject* self);
    virtual ~APlayerController() {}
    
    virtual void Tick(float deltaTime);
    DECL_REP();

    TPtr<CPlayer> GetPlayer() const { return mPlayer.Lock(); }
    void SetPlayer(TPtr<CPlayer> const& value) { mPlayer = value; }
    TPtr<ACamera> GetCamera() const { return mCamera.Lock(); }
    void SetCamera(TPtr<ACamera> const& value) { mCamera = value; mNetDirty = true; }

    friend void _InitActorClasses();
    friend void _InitActorBindings();
};

class KH_ENGINE_API APawn: public AComponent {
    TWeakPtr<AController> mController;

    KH_DECL_GC()

protected:
    void InitImpl(TPtr<CActor> owner);
    void ExitImpl();

    DECL_CLASSINFO()

public:
    APawn(PyObject* self);
    virtual ~APawn() {}
    
    virtual void Tick(float deltaTime);
    DECL_REP();

    //void _Possessed(TPtr<AController> controller) {}
    //void _Unpossessed() {}
    void PossessedBy(TPtr<AController> controller);
    void Unpossessed();

    TPtr<AController> GetController() const { return mController.Lock(); }
    void SetController(TPtr<AController> c) { mController = c; mNetDirty = true; }

    friend void _InitActorClasses();
    friend void _InitActorBindings();
};

inline void APawn::PossessedBy(TPtr<AController> controller) {
    call_method<void>(mSelf, "PossessedBy", controller);
}

inline void APawn::Unpossessed() {
    call_method<void>(mSelf, "Unpossessed");
}

struct FRepData {
    TWeakPtr<AComponent> Component;
    std::vector<byte> Recent;
    std::vector<sint16> Indices;
    std::vector<FFieldInfo const*> Fields;
    uint8 Index;
};

struct FRepChange {
    bool Added;
    uint8 Index;
    object ClassName;
};

class FReplicaManager;
class FRMConnection;

class KH_ENGINE_API ANetwork: public AComponent, public rn::Replica3 {
    bool mHasNativeRep;
    bool mUseNativeRep;
    bool mHasPythonRep;
    bool mUsePythonRep;
    bool mHasSentInitialFields;
    bool mUpdatingComps;

    // Native replication
    std::vector<FRepData> mData;
    std::vector<FRepChange> mChanges;
    ENetRole::Type mRecentRole;
    ENetRole::Type mRecentRemoteRole;
    TWeakPtr<CActor> mRecentOwner;
    unicode mRecentTag;

    // Python Replication
    //tuple mNetVarsList;
    //dict mNetVarsDict;
    //list mRecentPython;
    //object mPickleFunc;
    //object mPickleProtocol;

    bool InitNativeRepComp(TPtr<AComponent> comp);
    void CleanupNativeRepComp(TPtr<AComponent> comp);
    bool InitPythonRepComp(TPtr<AComponent> comp) {
        return call_method<bool>(mSelf, "InitPythonRepComp", comp);
    }
    void CleanupPythonRepComp(TPtr<AComponent> comp) {
        call_method<void>(mSelf, "CleanupPythonRepComp", comp);
    }

protected:
    object ReplicatePython();
    void UpdatePython(object datastr);
    object PickleDumpImpl(object);

    void Reference();
    void Dereference();

    void InitImpl(TPtr<CActor> owner);
    void ExitImpl();

public:
    ANetwork(PyObject* self);
    virtual ~ANetwork();

    virtual void Tick(float deltaTime) {}

    void InitNativeRep();
    void ExitNativeRep();
    void InitPythonRep();
    void ExitPythonRep();
    bool Replicate(rn::BitStream* out, FRMConnection* dest, bool initial, bool nofields=false);
    bool Update(rn::BitStream* in, FRMConnection* src);
    void InitedComponent(TPtr<AComponent> component);
    void ExitedComponent(TPtr<AComponent> component);
    int GetNativeRepIndex(TPtr<AComponent> component);
    TPtr<AComponent> GetNativeRepComp(int index);
    bool GetIsRelevantFor(FRMConnection* conn, FReplicaManager* rm);
    //bool GetIsRelevantFor(TPtr<APlayerController> const& viewer,
    //                      TPtr<CActor> const& realViewer,
    //                      FVector3F const& position);

    bool GetUseNativeRep() const { return mUseNativeRep; }
    void SetUseNativeRep(bool v) { mUseNativeRep = v; }

    rn::ReplicaManager3* GetReplicaManager() const { return this->replicaManager; }

    // Replica3 methods
    virtual void WriteAllocationID(rn::Connection_RM3* dest, rn::BitStream* out) const;
    virtual rn::RM3ConstructionState QueryConstruction(rn::Connection_RM3* dest, rn::ReplicaManager3* rm);
    virtual rn::RM3DestructionState QueryDestruction(rn::Connection_RM3 *dest, rn::ReplicaManager3 *rm);
    virtual bool QueryRemoteConstruction(rn::Connection_RM3* source);
    virtual void SerializeConstruction(rn::BitStream* bs, rn::Connection_RM3* dest);
    virtual bool DeserializeConstruction(rn::BitStream* bs, rn::Connection_RM3* source);
    virtual void SerializeDestruction(rn::BitStream* bs, rn::Connection_RM3* dest);
    virtual bool DeserializeDestruction(rn::BitStream* bs, rn::Connection_RM3* source);
    virtual rn::RM3ActionOnPopConnection QueryActionOnPopConnection(RakNet::Connection_RM3 *dropped) const;
    virtual rn::RM3QuerySerializationResult QuerySerialization(RakNet::Connection_RM3 *dest);
    virtual rn::RM3SerializationResult Serialize(rn::SerializeParameters* sp);
    virtual void Deserialize(rn::DeserializeParameters* dp);
    virtual void DeallocReplica(rn::Connection_RM3* source);
    virtual void BroadcastDestruction();

    friend void _InitActorClasses();
    friend void _InitActorBindings();
    friend void _ExitActorClasses();
};

inline void ANetwork::InitPythonRep() {
    call_method<void>(mSelf, "InitPythonRep");
}

inline void ANetwork::ExitPythonRep() {
    call_method<void>(mSelf, "ExitPythonRep");
}

inline object ANetwork::ReplicatePython() {
    return call_method<object>(mSelf, "ReplicatePython");
}

inline void ANetwork::UpdatePython(object datastr) {
    call_method<void>(mSelf, "UpdatePython", datastr);
}

inline object ANetwork::PickleDumpImpl(object obj) {
    return call_method<object>(mSelf, "PickleDumpImpl", obj);
}

/// An entry in CActor's component list.
/// This provides easy access to the component's type and support for sorting.
/// Instances of this class must always point to a valid component and type.
struct FComponentEntry {
    /// The held component
    TPtr<AComponent> Component;
    /// The Python type of the component
    handle<PyTypeObject> Type;

    FComponentEntry(TPtr<AComponent> c):
        Component(c),
        Type(c->GetTypeHandle()) {}

    /// Sort by priority support.
    bool operator<(FComponentEntry const& other) const { return *Component < *other.Component; }

    bool operator==(FComponentEntry const& other) const { return Type.get() == other.Type.get(); }
    bool operator!=(FComponentEntry const& other) const { return Type.get() != other.Type.get(); }

    operator TPtr<AComponent>() const { return Component; }
};

/// Component slot index. Names are not all-caps in order to be macro-compatible
/// This must be kept in synch with the type_info list in CActor::AddComponent()
/// Slotted components are accessed through GetTYPENAME()
enum EComponentSlot {
    SLOT_Transform = 0,
    SLOT_Movement,
    SLOT_Render,
    SLOT_Collider,
    SLOT_Camera,
    SLOT_Pawn,
    SLOT_PlayerController,
    SLOT_Network,
    SLOT_LAST
};

#define GET_SLOT(TYPE) \
    inline const TPtr<A##TYPE>& Get##TYPE() const { \
        return (TPtr<A##TYPE>&)mComponentSlots[(uint)SLOT_##TYPE]; \
    }

/// The main component container class.
/// Represents a single object in the game level composed of components.
class KH_ENGINE_API CActor: public core::CObject {
public:
    typedef std::vector<TWeakPtr<CActor>> ChildrenType;
    typedef std::vector<FComponentEntry> ComponentsType;

private:
    DECL_PICKLER_NOINIT_N(CActor, tuple);
    /// Per-level unique non-zero ID
    uint32 mID;
    /// Per-level unique name, unicode or None
    object mName;
    /// The actor's tag.
    unicode mTag;
    /// The level that owns this actor. An actor is considered invalid if
    /// it has no level.
    TWeakPtr<CLevel> mLevel;
    /// The actor that owns this actor
    TWeakPtr<CActor> mOwner;
    /// The actors owned by this actor
    ChildrenType mChildren;
    /// True when the actor needs to be deleted at the first opportunity.
    bool mDeleteMe;

    /// The components held by the actor. The most derived type must be unique.
    ComponentsType mComponents;
    /// Fast lookup slots for common components. This is synchronized with
    /// EComponentSlot.
    TPtr<AComponent> mComponentSlots[SLOT_LAST];

    ENetRole::Type mRole;
    ENetRole::Type mRemoteRole;
    bool mAlwaysRelevant;
    bool mOnlyRelevantToOwner;

    void SetLevel(TPtr<CLevel> const& level);
    void SetName(object const& name);

    KH_DECL_GC()

protected:
    //DECL_CLASSINFO()
    /// Handles for the types allowed for the component slots.
    /// AddComponent() will allow subclasses of these types in the slots.
    static handle<PyTypeObject> msComponentSlotTypes[SLOT_LAST];

public:
    CActor(PyObject* self);
    virtual ~CActor();

    /// Update the actor's components
    virtual void Tick(float deltaTime);
    /// Get a component using its Python type. Allows subclasses.
    TPtr<AComponent> GetComponent(object cls) const;
    
    TPtr<AComponent> GetComponent(py::type_info info) const;

    template <typename T>
    TPtr<T> GetComponent() const {
        return core::CastPtrDynamic<T>(GetComponent(py::type_id<T>()));
    }

    GET_SLOT(Transform)
    GET_SLOT(Movement)
    GET_SLOT(Render)
    GET_SLOT(Collider)
    GET_SLOT(Camera)
    GET_SLOT(Pawn)
    GET_SLOT(PlayerController)
    GET_SLOT(Network)

    /// Add a component to this actor. Calls OnInit() on the component.
    void AddComponent(TPtr<AComponent> const&);
    /// Remove a component from this actor. Calls OnExit() on the component.
    void RemoveComponent(TPtr<AComponent> const&);
    /// Call OnExit() for all components and remove them.
    void ClearComponents();

    void Init(uint32 id, object const& name, TPtr<CLevel> const& level, TPtr<CActor> const& owner);
    void Exit();
    void GainedChild(TPtr<CActor> const& actor);
    void LostChild(TPtr<CActor> const& actor);
    void SetChildOwners(TPtr<CActor> const& actor);
    void UpdateNetworkComponent();
    void SendMessageA(str name, tuple args=make_tuple(), object kwds=object()) {
        call_method<void>(mSelf, "SendMessageA", name, args, kwds);
    }
    bool Rename(object const& newname);

    KH_DEF_GET(uint32, ID)
    object const& GetName() const { return mName; }
    KH_DEF_GETSET(unicode const&, Tag)
    TPtr<CLevel> GetLevel() const { return mLevel.Lock(); }
    TPtr<CActor> GetOwner() const { return mOwner.Lock(); }
    void SetOwner(TPtr<CActor> const&);
    bool GetNoDelete() const;
    KH_DEF_GET(bool, DeleteMe)
    KH_DEF_GET(ComponentsType const&, Components)

    uint32 GetComponentCount() const {
        return mComponents.size();
    }

    TPtr<AComponent> GetComponent(uint32 index) const {
        return mComponents.at(index).Component;
    }

    uint32 GetChildCount() const {
        return mChildren.size();
    }

    TPtr<CActor> GetChild(uint32 index) const {
        return mChildren.at(index).Lock();
    }

    TPtr<CActor> GetTopOwner() {
        TPtr<CActor> owner, nextowner = GetOwner();
        while (nextowner) {
            owner = nextowner;
            nextowner = nextowner->GetOwner();
        }
        return owner;
    }

    bool IsOwnedBy(TPtr<CActor> const& other) {
        for (TPtr<CActor> test = GetSelfPtr<CActor>(); test; test = test->GetOwner())
            if (test == other)
                return true;
        return false;
    }

    ENetRole::Type GetRole() const { return mRole; }
    void SetRole(ENetRole::Type role);
    ENetRole::Type GetRemoteRole() const { return mRemoteRole; }
    void SetRemoteRole(ENetRole::Type role);
    bool GetAlwaysRelevant() const { return mAlwaysRelevant; }
    void SetAlwaysRelevant(bool value) { mAlwaysRelevant = value; }
    bool GetOnlyRelevantToOwner() const { return mOnlyRelevantToOwner; }
    void SetOnlyRelevantToOwner(bool value) { mOnlyRelevantToOwner = value; }

    friend void _InitActorClasses();
    friend void _InitActorBindings();
    friend void _ExitActorClasses();
    friend class CLevel;
};

#undef GET_SLOT

inline bool CActor::GetNoDelete() const {
    for (uint i = 0; i < mComponents.size(); i++)
        if (mComponents[i].Component->GetNoDelete())
            return true;
    return false;
}

//}

}} // namespace Kharlia

ENABLE_BACK_REFERENCE(kharlia::engine::AComponent)
ENABLE_BACK_REFERENCE(kharlia::engine::ATransform)
ENABLE_BACK_REFERENCE(kharlia::engine::AMovement)
ENABLE_BACK_REFERENCE(kharlia::engine::ARender)
ENABLE_BACK_REFERENCE(kharlia::engine::ASprite)
ENABLE_BACK_REFERENCE(kharlia::engine::ASpriteAnimation)
ENABLE_BACK_REFERENCE(kharlia::engine::ACollider)
ENABLE_BACK_REFERENCE(kharlia::engine::ACamera)
ENABLE_BACK_REFERENCE(kharlia::engine::AController)
ENABLE_BACK_REFERENCE(kharlia::engine::APlayerController)
ENABLE_BACK_REFERENCE(kharlia::engine::APawn)
ENABLE_BACK_REFERENCE(kharlia::engine::ANetwork)
ENABLE_BACK_REFERENCE(kharlia::engine::CActor)

#endif